package com.zzs.judge.config;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.AccessMode;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.zzs.judge.constants.DockerConsts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.URI;
import java.time.Duration;

/**
 * @author zzs
 * @date 2025/03/28
 */
@Configuration
public class DockerConfig {
    // Docker Daemon 的地址，Windows 上通常使用 tcp://localhost:2375
    @Value("${docker.host}")
    private String dockerHost;

    // 连接超时时间（秒）
    @Value("${docker.connection-timeout}")
    private int connectionTimeout;

    // 响应超时时间（秒）
    @Value("${docker.response-timeout}")
    private int responseTimeout;

    // 最大连接数
    @Value("${docker.max-connection}")
    private int maxConnections;

    // 限制物理内存
    @Value("${docker.memory}")
    private long memory;

    @Value("${docker.memory-swap}")
    private long memorySwap;

    @Value("${docker.cpu-count}")
    private long cpuCount;

    /**
     * 配置 DockerClient
     *
     * @return DockerClient 实例
     */
    @Bean
    public DockerClient dockerClient() {
        // 配置 Docker 连接信息
        DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig
                .createDefaultConfigBuilder()
                // 设置 Docker Daemon 地址
                .withDockerHost(dockerHost)
                .build();
        // 配置 HTTP 客户端
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(URI.create(dockerHost))
                // 这里未启用 TLS
                .sslConfig(dockerClientConfig.getSSLConfig())
                // 最大连接数
                .maxConnections(maxConnections)
                // 连接超时
                .connectionTimeout(Duration.ofSeconds(connectionTimeout))
                // 响应超时
                .responseTimeout(Duration.ofSeconds(responseTimeout))
                .build();
        return DockerClientImpl.getInstance(dockerClientConfig, httpClient);
    }

    /**
     * 容器配置
     * withReadonlyRootfs(true) 保护了容器操作系统层面，防止恶意代码修改容器的基础环境，但并不能阻止容器对挂载的 本地代码目录 进行写操作。
     * AccessMode.ro 确保挂载的 本地代码目录 是只读的，从而防止用户代码修改上传的代码、数据或配置文件。
     * 同时配置这两个选项，以确保 容器环境的安全 和 挂载目录的保护
     */
    @Bean
    public HostConfig hostConfig() {
//         定义挂载，将本地代码目录映射到容器内, AccessMode.ro 确保挂载的 本地代码目录 是只读的
//        Bind bind = new Bind(DockerConsts.LOCAL_CODE_DIR, new Volume(DockerConsts.DOCKER_CODE_DIR), AccessMode.ro);
        Bind bind = new Bind(DockerConsts.LOCAL_CODE_DIR, new Volume(DockerConsts.DOCKER_CODE_DIR), AccessMode.rw);
        return HostConfig.newHostConfig()
                // 设置卷挂载，容器内可访问外部代码文件
                .withBinds(bind)
                // 设置只读文件系统，防止用户篡改环境
                .withReadonlyRootfs(true)
                // 禁用网络，防止恶意代码访问外网
                .withNetworkMode("none")
                // 限制物理内存 512MB
                .withMemory(memory)
                // 禁用 Swap，防止使用额外虚拟内存
                .withMemorySwap(memorySwap)
                // 限制 CPU 使用 1 核心
                .withCpuCount(cpuCount)
                // CPU 配额周期（默认 100ms）, 表示时间片的长度
                .withCpuPercent(100000L)
                // 每个CPU配额周期内允许使用的CPU时间
//                通过withCpuPercent和withCpuQuota, 在每个100ms的时间片里, 只能使用50ms的cpu时间
                .withCpuQuota(50000L)
                // 允许 OOM 时杀死进程，避免死循环占满内存
                .withOomKillDisable(false);
//                一个容器里进程数量限制
//                .withPidsLimit(50L);
    }
}
